----------------------------------------------------------------------------
-- Entity class
-- @classmod Entity
-- @author v.v.balashoff
-- @license CC BY-NC-SA 4.0
----------------------------------------------------------------------------

local Class = require('leviathan/Class')
local Log = require('leviathan/Log')

local Entity = Class:derive('Entity')

--- Create new entity.
-- Create new instance of entity.
-- @function new
-- @tparam string domain Name of domain.
-- @tparam string name Name of entity.
-- @tparam[opt] int x X coordinate of entity (default: 0).
-- @tparam[opt] int y Y coordinate of entity (default: 0).
-- @tparam[opt] int width Width of entity (default: 0).
-- @tparam[opt] int height Height of entity (default: 0).
-- @tparam[opt] int alpha Alpha of entity (default: 1 -- fully visible).
function Entity:new(domain, name, x, y, width, height, alpha)
    self.name = name
    self.domain = domain
    self.components = {}
    self.parent = nil
    self.childs = {}
    self.drawable = false
    self.enabled = true
    self.x = math.floor(x or 0)
    self.y = math.floor(y or 0)
    self.width = math.floor(width or 0)
    self.height = math.floor(height or 0)
    self.worldX = self.x
    self.worldY = self.y
    self.alpha = alpha or 1
    -- add entity to domain
    self.priority = 500
    domain:addEntity(self)
end

--- Add component to entity.
-- Add new component of entity.
-- @function addComponent
-- @tparam table c Instance of component (table address).
function Entity:addComponent(component)
    component.entity = self
    if self.components[component.id] then
        -- TODO remove it
        local s = 'Entity: '..self.name..'. Component ['..component.id..'] already exists! Name is «'..component.name..'»'
        Log:error(s)
        return
    end
    -- set priority for component if there is none
    if not component.priority then
        component.priority = self.priority
    end
    self.components[component.id] = component
    self.domain:rebuildCache()
end

--- Priority compare function.
-- Compare priority to sort elements. Takes 'element.priority' as priority value.
-- @function priorityCompare
-- @tparam table e1 Instance of element (table address).
-- @tparam table e2 Instance of element (table address).
local function priorityCompare(e1, e2)
    return e1.priority < e2.priority
end

--- Add child to entity.
-- Add child to entity.
-- @function addChild
-- @tparam table child Instance of entity (table address).
function Entity:addChild(child)
    child.parent = self
    self.childs[#self.childs + 1] = child
    if #self.childs > 1 then
        table.sort(self.childs, priorityCompare)
    end
end

--- Get entity component.
-- Get entity component by it's name.
-- @function getComponent
-- @tparam string name Name of component to get.
-- @treturn table Return component (table address) or nil.
function Entity:getComponent(name)
    local comp = nil
    for _, c in pairs(self.components) do
        if c.name == name then
            comp = c
        end
    end
    return comp
end

--- Get entity world coordinates (x, y).
-- Get entity world coordinates (x, y). Calculate all parents coordinates and add current ones.
-- @function getWorldTransform
-- @treturn int Return X world coordinate of entity.
-- @treturn int Return Y world coordinate of entity.
function Entity:getWorldTransform()
    local x = self.x
    local y = self.y
    local finish = false
    local curr = self
    repeat
        if not curr.parent then
            finish = true
        else
            curr = curr.parent
            x = x + curr.worldX
            y = y + curr.worldY
        end
    until finish
    return x, y
end

--- Delete component from entity.
-- Delete component from entity. Call rebuildCache domain function.
-- @function deleteComponent
-- @tparam table c Instance of component (table address).
function Entity:deleteComponent(c)
    self.components[c.id] = nil
    self.domain:rebuildCache()
end

--- Set entity priority.
-- Set priority for entity. Set same priority for all entity components as well. Call rebuildCache domain function.
-- @function setPriority
-- @tparam int p Priority to set.
function Entity:setPriority(p)
    self.priority = p
    -- set new entity priority for all components
    for _, c in pairs(self.components) do
        c.priority = p
    end
--    self.domain:orderComponents()
    self.domain:rebuildCache()
end

-- TODO debug only

--- Dump entity.
-- Dump information about current entity.
-- @function dump
function Entity:dump()
    print('===========================')
    print('\27[32mEntity: \27[0m'..self.name..' ('..tostring(self)..')')
    local p = ''
    if self.parent then
        p = self.parent.name
    end
    print('  \27[32mparent: \27[0m'..p)
    print('  \27[32mprioriy: \27[0m'..self.priority)
--    print('---------------------------')
    local d = self.domain.name or ''
    print('  \27[32mDomain: \27[0m'..d)
    print('  \27[32menabled: \27[0m', self.enabled)
    print('---------------------------')
    print('  \27[32mComponents: \27[0m')
    for k, v in pairs(self.components) do
        print('  '..k..': '..v.name..' ('..tostring(v)..')')
        for n, m in pairs(v) do
        print('  ', n, m)
--            print('      '..m.priority, m.enabled, m.name..' ['..m.entity.name..']')
        end
    end
    print('---------------------------')
    print('  \27[32mChilds:\27[0m')
    for k, v in pairs(self.childs) do
        print(k, v.name, tostring(v))
    end
    print('---------------------------')
    print('===========================')
end

return Entity
